/**
 * 
 */
package it.uniroma3.engine.searcher;

import it.uniroma3.engine.factory.DirectoryFactory;
import it.uniroma3.engine.model.CandidateProteinName;
import it.uniroma3.engine.paramer.EngineParameter;
import it.uniroma3.engine.util.Utils;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.FuzzyQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.WildcardQuery;

/**
 * @author Accosta Augusto
 * @author Lopez Elisabetta
 *
 */
public class Searcher {

	private IndexSearcher indexSearcher;
	
	/**
	 * Constructor
	 */
	public Searcher() {
		try {
			this.setIndexSearcher(getIndexSearcher());
			
		} catch (CorruptIndexException e) {
			// TODO Auto-generated catch block
			System.err.println("Fail! - IndexSearcher creation! " + e.getMessage());
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			System.err.println("Fail! - IndexSearcher creation! " + e.getMessage());
			e.printStackTrace();
		}
	}

	/**
	 * This method search in the index the given abbreviation result and it's expansion
	 * 
	 * @param abbreviationResult
	 * @return the list of candidates proteins names
	 */
	public List<CandidateProteinName> search(String abbreviationResult) {
		List<CandidateProteinName> candidatesList = null;
		
		String spelledOutAbbreviationResult = Utils.spelledOut(abbreviationResult);
		// TODO trasformare gli score in percentuale
		try {
			candidatesList = new LinkedList<CandidateProteinName>();

//			Query multipleFieldQueryFullName = new MultiFieldQueryParser(EngineParameter.getVersion(), new String[] {EngineParameter.CONTENTS_FULLNAME, EngineParameter.CONTENTS_SPELLED_OUT_FULLNAME}, EngineParameter.ANALYZER).parse(abbreviationResult);
//			candidatesList.addAll(this.executeQuery(multipleFieldQueryFullName, EngineParameter.NUMBER_OF_EXTRACTED_CANDIDATES));
			
//			Query multipleFieldQuerySpelledOutFullName = new MultiFieldQueryParser(EngineParameter.getVersion(), new String[] {EngineParameter.CONTENTS_FULLNAME, EngineParameter.CONTENTS_SPELLED_OUT_FULLNAME}, EngineParameter.ANALYZER).parse(spelledOutAbbreviationResult);
//			candidatesList.addAll(this.executeQuery(multipleFieldQuerySpelledOutFullName, EngineParameter.NUMBER_OF_EXTRACTED_CANDIDATES));
			
//			PhraseQuery phraseQuery = new PhraseQuery();
//			phraseQuery.add(new Term(EngineParameter.CONTENTS_FULLNAME, abbreviationResult));
//			candidatesList.addAll(this.executeQuery(phraseQuery, EngineParameter.NUMBER_OF_EXTRACTED_CANDIDATES, 1.3f));
			
			/* QueryParser in CONTENTS_FULLNAME  */
			Query queryFullName = new QueryParser(EngineParameter.getVersion(), EngineParameter.CONTENTS_FULLNAME, EngineParameter.ANALYZER).parse(abbreviationResult);
			candidatesList.addAll(this.executeQuery(queryFullName, EngineParameter.NUMBER_OF_EXTRACTED_QUERYPARSER_CANDIDATES, 2.0f));
			
			/* QueryParser in CONTENTS_SPELLED_OUT_FULLNAME  */
			Query querySpelledOutFullName = new QueryParser(EngineParameter.getVersion(), EngineParameter.CONTENTS_SPELLED_OUT_FULLNAME, EngineParameter.ANALYZER).parse(spelledOutAbbreviationResult);
			candidatesList.addAll(this.executeQuery(querySpelledOutFullName, EngineParameter.NUMBER_OF_EXTRACTED_QUERYPARSER_CANDIDATES, 1.5f));
			
			Query fuzzyQueryFullName = new FuzzyQuery(new Term(EngineParameter.CONTENTS_FULLNAME, abbreviationResult));
			candidatesList.addAll(this.executeQuery(fuzzyQueryFullName, EngineParameter.NUMBER_OF_EXTRACTED_FUZZY_CANDIDATES, 1.0f));			
			
			if(candidatesList.size() == 0) {
				Query wildCardQuery = new WildcardQuery(new Term(EngineParameter.CONTENTS_FULLNAME, abbreviationResult.replaceAll(" ", "*")));
				candidatesList.addAll(this.executeQuery(wildCardQuery, EngineParameter.NUMBER_OF_EXTRACTED_WILDCARD_CANDIDATES, 1.0f));
			}
				
		} catch (IOException e) {
			// TODO Auto-generated catch block
			System.err.println("Searching failure!" + e.getMessage());
			e.printStackTrace();
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			System.err.println("Searching failure!" + e.getMessage());
			e.printStackTrace();
		}
		return candidatesList;
	}
	
	/**
	 * This method execute the defined query into the search method
	 * It retrieve the documents and the scores of each potentially candidates 
	 * 
	 * @param definedQuery
	 * @param numbeOfCandidatesToExtract
	 * @return
	 * @throws CorruptIndexException
	 * @throws IOException
	 */
	private List<CandidateProteinName> executeQuery(Query definedQuery, int numbeOfCandidatesToExtract, float boostScoreFactor) throws CorruptIndexException, IOException {
		List<CandidateProteinName> partialListOfCandidates = new LinkedList<CandidateProteinName>();
		
		TopDocs candidates = this.indexSearcher.search(definedQuery, numbeOfCandidatesToExtract);
//		float maxScore = candidates.getMaxScore();
		
		ScoreDoc[] hits = candidates.scoreDocs;
		
		for (int i = 0; i < hits.length; i++) {
			if(hits[i].score > EngineParameter.OUTPUT_THRESHOLD) {
				partialListOfCandidates.add(new CandidateProteinName(this.indexSearcher.doc(hits[i].doc).get(EngineParameter.CONTENTS_FULLNAME), (hits[i].score*boostScoreFactor))); // CONTENTS_FULLNAME never change
//				this.indexSearcher.doc(hits[i].doc).get("proteinnameid"); // da questo poi posso istanziare gli oggetti relativi e tutto il resto compresi i join sulle 2 tabelle
			}
		}
		
		return partialListOfCandidates;
	}
	
	/**
	 * This method retrieve the actual {@code IndexSearch}
	 * 
	 * @return the indexSearcher
	 * @throws IOException 
	 * @throws CorruptIndexException 
	 */
	public IndexSearcher getIndexSearcher() throws CorruptIndexException, IOException {
		return new IndexSearcher(DirectoryFactory.getFSDirectory());
	}
	
	/**
	 * @param indexSearcher the indexSearcher to set
	 */
	public void setIndexSearcher(IndexSearcher indexSearcher) {
		this.indexSearcher = indexSearcher;
	}

}
